package com.moorror.codegenvel.utils;

import com.intellij.codeInsight.actions.AbstractLayoutCodeProcessor;
import com.intellij.codeInsight.actions.ReformatCodeProcessor;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.util.ExceptionUtil;
import com.moorror.codegenvel.common.dialog.CompareFileUtils;
import com.moorror.codegenvel.common.dialog.MessageDialogUtils;
import com.moorror.codegenvel.constant.CodeGenByVelocityConstant;
import com.moorror.codegenvel.domain.bean.savefile.SaveFileDesc;
import org.apache.commons.collections4.CollectionUtils;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.List;

/**
 * @Author jiyanghuang
 * @Date 2025/8/17 20:52
 */
public class FileUtils {

    private static final Logger LOG = Logger.getInstance(FileUtils.class);
    private static volatile FileUtils fileUtils;

    /**
     * 单例模式
     */
    public static FileUtils getInstance() {
        if (fileUtils == null) {
            synchronized (FileUtils.class) {
                if (fileUtils == null) {
                    fileUtils = new FileUtils();
                }
            }
        }
        return fileUtils;
    }

    private FileUtils() {
    }

    /**
     * 创建子目录
     *
     * @param project 文件对象
     * @param parent  父级目录
     * @param dirName 子目录
     * @return 目录对象
     */
    public static VirtualFile createChildDirectory(Project project, VirtualFile parent, String dirName) {
        return WriteCommandAction.runWriteCommandAction(project, (Computable<VirtualFile>) () -> {
            try {
                return VfsUtil.createDirectoryIfMissing(parent, dirName);
            } catch (IOException e) {
                Messages.showWarningDialog("目录创建失败：" + dirName, CodeGenByVelocityConstant.TITLE_INFO);
                return null;
            }
        });
    }

    /**
     * 创建子文件
     *
     * @param project  项目对象
     * @param parent   父级目录
     * @param fileName 子文件名
     * @return 文件对象
     */
    public static VirtualFile createChildFile(Project project, VirtualFile parent, String fileName) {
        return WriteCommandAction.runWriteCommandAction(project, (Computable<VirtualFile>) () -> {
            PsiManager psiManager = PsiManager.getInstance(project);
            try {
                PsiDirectory directory = psiManager.findDirectory(parent);
                if (directory != null) {
                    PsiFile psiFile = directory.createFile(fileName);
                    return psiFile.getVirtualFile();
                }
                return parent.createChildData(new Object(), fileName);
            } catch (IOException e) {
                Messages.showWarningDialog("文件创建失败：" + fileName, CodeGenByVelocityConstant.TITLE_INFO);
                return null;
            }
        });
    }

    /**
     * 设置文件内容
     *
     * @param project 项目对象
     * @param file    文件
     * @param text    文件内容
     * @return 覆盖后的文档对象
     */
    public static Document writeFileContent(Project project, VirtualFile file, String fileName, String text) {
        FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
        Document document = fileDocumentManager.getDocument(file);
        if (document == null) {
            WriteCommandAction.runWriteCommandAction(project, () -> {
                try {
                    file.setBinaryContent(text.getBytes());
                } catch (IOException e) {
                    throw new IllegalStateException("二进制文件写入失败，fileName：" + fileName);
                }
            });
            return fileDocumentManager.getDocument(file);
        }
        WriteCommandAction.runWriteCommandAction(project, () -> document.setText(text));
        PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project);
        // 提交改动，并非VCS中的提交文件
        psiDocumentManager.commitDocument(document);
        return document;
    }

    /**
     * 格式化虚拟文件
     *
     * @param project     项目对象
     * @param virtualFile 虚拟文件
     */
    public static void reformatFile(Project project, VirtualFile virtualFile) {
        PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
        if (psiFile == null) {
            return;
        }
        reformatFile(project, Collections.singletonList(psiFile));
    }

    /**
     * 执行格式化
     *
     * @param project     项目对象
     * @param psiFileList 文件列表
     */
    @SuppressWarnings("unchecked")
    public static void reformatFile(Project project, List<PsiFile> psiFileList) {
        if (CollectionUtils.isEmpty(psiFileList)) {
            return;
        }
        // 尝试对文件进行格式化处理
        AbstractLayoutCodeProcessor processor = new ReformatCodeProcessor(project, psiFileList.toArray(new PsiFile[0]), null, false);
        // 优化导入，有时候会出现莫名其妙的问题，暂时关闭
//        processor = new OptimizeImportsProcessor(processor);
        // 重新编排代码（会将代码中的属性与方法的顺序进行重新调整）
//            processor = new RearrangeCodeProcessor(processor);

        // 清理代码，进行旧版本兼容，旧版本的IDEA尚未提供该处理器
        try {
            Class<AbstractLayoutCodeProcessor> codeCleanupCodeProcessorCls = (Class<AbstractLayoutCodeProcessor>) Class.forName("com.intellij.codeInsight.actions.CodeCleanupCodeProcessor");
            Constructor<AbstractLayoutCodeProcessor> constructor = codeCleanupCodeProcessorCls.getConstructor(AbstractLayoutCodeProcessor.class);
            processor = constructor.newInstance(processor);
        } catch (ClassNotFoundException ignored) {
            // 类不存在直接忽略
        } catch (NoSuchMethodException | IllegalAccessException | InstantiationException |
                 InvocationTargetException e) {
            // 抛出未知异常
            ExceptionUtil.rethrow(e);
        }
        // 执行处理
        processor.run();
    }

    /**
     * 通过IDEA自带的Psi文件方式写入
     */
    public static void write(SaveFileDesc saveFileDesc) {
//        if (!Boolean.TRUE.equals(callback.getWriteFile())) {
//            return;
//        }
        // 判断目录是否存在
        VirtualFile baseDir = ProjectUtils.fetchBaseDir(saveFileDesc.getProject());
        if (baseDir == null) {
            throw new IllegalStateException("项目基本路径不存在");
        }
        // 处理保存路径
        String savePath = handlerPath(saveFileDesc.getSavePath(), false);
//        if (isProjectFile()) {
//            // 删除保存路径的前面部分
//            savePath = savePath.substring(handlerPath(baseDir.getPath()).length());
//        } else {
//            baseDir = null;
//        }
        savePath = savePath.substring(handlerPath(baseDir.getPath()).length());

        // 删除开头与结尾的/符号
        while (savePath.startsWith("/")) {
            savePath = savePath.substring(1);
        }
        while (savePath.endsWith("/")) {
            savePath = savePath.substring(0, savePath.length() - 1);
        }
        // 查找保存目录是否存在
        VirtualFile saveDir;
        if (baseDir == null) {
            saveDir = VfsUtil.findFileByIoFile(new File(savePath), false);
        } else {
            saveDir = VfsUtil.findRelativeFile(baseDir, savePath.split("/"));
        }
        // 提示创建目录
        VirtualFile directory = titleCreateDir(saveFileDesc, saveDir, baseDir, savePath);
        if (directory == null) {
            return;
        }
        VirtualFile psiFile = directory.findChild(saveFileDesc.getFileName());
        // 保存或覆盖
        saveOrReplaceFile(saveFileDesc, psiFile, directory);
    }

    /**
     * 处理路径，统一分割符并转小写
     *
     * @param path 路径
     * @return 处理后的路径
     */
    public static String handlerPath(String path) {
        return handlerPath(path, true);
    }

    /**
     * 处理路径，统一分割符并转小写
     *
     * @param path      路径
     * @param lowerCase 是否转小写
     * @return 处理后的路径
     */
    public static String handlerPath(String path, boolean lowerCase) {
        // 统一分割符
        path = path.replace("\\", "/");
        // 避免重复分割符
        path = path.replace("//", "/");
        // 统一小写
        return lowerCase ? path.toLowerCase() : path;
    }

    /**
     * 提示创建目录
     *
     * @param saveDir 保存路径
     * @return 是否放弃执行
     */
    public static VirtualFile titleCreateDir(SaveFileDesc saveFileDesc, VirtualFile saveDir, VirtualFile baseDir, String savePath) {
        if (saveDir != null) {
            return saveDir;
        }
        // 尝试创建目录
        String msg = String.format("Directory %s Not Found, Confirm Create?", nextLineSplit(saveFileDesc.getSavePath(), 100));
        if (saveFileDesc.getGenerateOptions().getTitleSure()) {
            saveDir = fileUtils.createChildDirectory(saveFileDesc.getProject(), baseDir, savePath);
            return saveDir;
        } else if (saveFileDesc.getGenerateOptions().getTitleRefuse()) {
            return null;
        } else {
            if (MessageDialogUtils.yesNo(saveFileDesc.getProject(), msg)) {
                saveDir = fileUtils.createChildDirectory(saveFileDesc.getProject(), baseDir, savePath);
                return saveDir;
            }
        }
        return null;
    }

    /**
     * 保存或替换文件
     * todo 把 callback 对象去掉，默认填充一些字段
     *
     * @param file      文件
     * @param directory 目录
     */
    public static void saveOrReplaceFile(SaveFileDesc saveFileDesc, VirtualFile file, VirtualFile directory) {
        PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(saveFileDesc.getProject());
        Document document;
        // 文件不存在直接创建
        if (file == null) {
            file = fileUtils.createChildFile(saveFileDesc.getProject(), directory, saveFileDesc.getFileName());
            if (file == null) {
                return;
            }
            document = coverFile(saveFileDesc, file);
        } else {
            if (!saveFileDesc.isCoverPrompt()) {
                // 默认选是
                document = coverFile(saveFileDesc, file);
            } else {
                String msg = String.format("File %s Exists, Select Operate Mode?", nextLine(file, 100));
                int result = MessageDialogUtils.yesNoCancel(saveFileDesc.getProject(), msg, "Convert", "Compare", "Cancel");
                switch (result) {
                    case Messages.YES:
                        // 覆盖文件
                        document = coverFile(saveFileDesc, file);
                        break;
                    case Messages.NO:
                        // 对比代码时也格式化代码
                        String newText = saveFileDesc.getContent();
                        if (Boolean.TRUE.equals(saveFileDesc.getReformat())) {
                            // 保留旧文件内容，用新文件覆盖旧文件执行格式化，然后再还原旧文件内容
                            String oldText = fetchFileText(file);
                            Document tmpDoc = coverFile(saveFileDesc, file);
                            // 格式化代码
                            FileUtils.reformatFile(saveFileDesc.getProject(), file);
                            // 提交文档改动，并非VCS中的提交文件
                            psiDocumentManager.commitDocument(tmpDoc);
                            // 获取新的文件内容
                            newText = fetchFileText(file);
                            // 还原旧文件
                            coverFile(saveFileDesc, file, oldText);
                        }
                        FileType fileType = FileTypeManager.getInstance().getFileTypeByFileName(saveFileDesc.getFileName());
                        CompareFileUtils.showCompareWindow(saveFileDesc.getProject(), file, new LightVirtualFile(saveFileDesc.getFileName(), fileType, newText));
                        return;
                    case Messages.CANCEL:
                    default:
                        return;
                }
            }
        }
        // 执行代码格式化操作
        if (Boolean.TRUE.equals(saveFileDesc.getReformat())) {
            FileUtils.reformatFile(saveFileDesc.getProject(), file);
        }
        // 提交文档改动，并非VCS中的提交文件
        if (document != null) {
            psiDocumentManager.commitDocument(document);
        }
    }


    public static String fetchFileText(VirtualFile file) {
        FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
        Document document = fileDocumentManager.getDocument(file);
        if (document == null) {
            throw new IllegalStateException("virtual file to document failure");
        }
        return document.getText();
    }

    /**
     * 覆盖文件
     *
     * @param file 文件
     * @return 覆盖后的文档对象
     */
    public static Document coverFile(SaveFileDesc saveFileDesc, VirtualFile file) {
        return coverFile(saveFileDesc, file, saveFileDesc.getContent());
    }

    /**
     * 覆盖文件
     *
     * @param file 文件
     * @param text 文件内容
     * @return 覆盖后的文档对象
     */
    public static Document coverFile(SaveFileDesc saveFileDesc, VirtualFile file, String text) {
        return FileUtils.writeFileContent(saveFileDesc.getProject(), file, saveFileDesc.getFileName(), text);
    }


    public static String nextLine(VirtualFile file, int lineLen) {
        String filePathAll = file.getPath();
        String fileName = file.getName();
        String split = filePathAll.replace(fileName, "");
        StringBuilder sb = new StringBuilder();
        int start = 0;
        int end = lineLen;
        while (end < split.length()) {
            sb.append(split.substring(start, end)).append("\n");
            start = end;
            end = start + lineLen;
        }
        if (start < split.length()) {
            sb.append(split.substring(start, split.length())).append("\n");
        }
        sb.append(fileName);
        return sb.toString();
    }


    public static String nextLineSplit(String split, int lineLen) {
        StringBuilder sb = new StringBuilder();
        int start = 0;
        int end = lineLen;
        while (end < split.length()) {
            sb.append(split.substring(start, end)).append("\n");
            start = end;
            end = start + lineLen;
        }
        if (start < split.length()) {
            sb.append(split.substring(start, split.length())).append("\n");
        }
        return sb.toString();
    }
}
